package com.sxr.file.impl;

import com.sxr.file.FileHandler;
import com.sxr.file.FileRequest;
import com.sxr.file.FileResponse;

import java.io.InputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * 抽象文件处理器
 * 处理请求头、响应头等公共逻辑，子类只需专注于文件读写
 * 
 * @author SXR
 * @since 1.0.0
 */
public abstract class AbstractFileHandler implements FileHandler {
    
    /** 默认缓冲区大小 */
    protected static final int DEFAULT_BUFFER_SIZE = 8192;
    
    /** 支持的MIME类型映射 */
    private static final Map<String, String> MIME_TYPE_MAP = new HashMap<>();
    
    static {
        // 图片类型
        MIME_TYPE_MAP.put("jpg", "image/jpeg");
        MIME_TYPE_MAP.put("jpeg", "image/jpeg");
        MIME_TYPE_MAP.put("png", "image/png");
        MIME_TYPE_MAP.put("gif", "image/gif");
        MIME_TYPE_MAP.put("bmp", "image/bmp");
        MIME_TYPE_MAP.put("webp", "image/webp");
        MIME_TYPE_MAP.put("svg", "image/svg+xml");
        MIME_TYPE_MAP.put("ico", "image/x-icon");
        MIME_TYPE_MAP.put("avif", "image/avif");

        // 文档类型
        MIME_TYPE_MAP.put("pdf", "application/pdf");
        MIME_TYPE_MAP.put("doc", "application/msword");
        MIME_TYPE_MAP.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        MIME_TYPE_MAP.put("xls", "application/vnd.ms-excel");
        MIME_TYPE_MAP.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        MIME_TYPE_MAP.put("ppt", "application/vnd.ms-powerpoint");
        MIME_TYPE_MAP.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
        MIME_TYPE_MAP.put("txt", "text/plain");
        MIME_TYPE_MAP.put("rtf", "application/rtf");
        
        // 音视频类型
        MIME_TYPE_MAP.put("mp3", "audio/mpeg");
        MIME_TYPE_MAP.put("wav", "audio/wav");
        MIME_TYPE_MAP.put("flac", "audio/flac");
        MIME_TYPE_MAP.put("aac", "audio/aac");
        MIME_TYPE_MAP.put("mp4", "video/mp4");
        MIME_TYPE_MAP.put("avi", "video/x-msvideo");
        MIME_TYPE_MAP.put("mov", "video/quicktime");
        MIME_TYPE_MAP.put("wmv", "video/x-ms-wmv");
        MIME_TYPE_MAP.put("flv", "video/x-flv");
        MIME_TYPE_MAP.put("webm", "video/webm");
        MIME_TYPE_MAP.put("m3u8", "application/vnd.apple.mpegurl");
        MIME_TYPE_MAP.put("m3u", "application/vnd.apple.mpegurl");
        MIME_TYPE_MAP.put("ts", "video/MP2T");
        MIME_TYPE_MAP.put("m4s", "video/mp4");

        // 压缩文件类型
        MIME_TYPE_MAP.put("zip", "application/zip");
        MIME_TYPE_MAP.put("rar", "application/vnd.rar");
        MIME_TYPE_MAP.put("7z", "application/x-7z-compressed");
        MIME_TYPE_MAP.put("tar", "application/x-tar");
        MIME_TYPE_MAP.put("gz", "application/gzip");
        
        // 其他常用类型
        MIME_TYPE_MAP.put("json", "application/json");
        MIME_TYPE_MAP.put("xml", "application/xml");
        MIME_TYPE_MAP.put("html", "text/html");
        MIME_TYPE_MAP.put("css", "text/css");
        MIME_TYPE_MAP.put("js", "application/javascript");
    }
    
    @Override
    public FileResponse handleDownload(FileRequest request) {
        try {
            // 验证请求
            if (validateRequest(request)) {
                return FileResponse.error(400, "Invalid request");
            }
            
            // 获取文件信息
            FileInfo fileInfo = getFileInfo(request);
            if (fileInfo == null) {
                return FileResponse.notFound();
            }
            
            // 检查文件权限
            if (!checkFilePermission(request, fileInfo)) {
                return FileResponse.error(403, "Access denied");
            }
            
            // 读取文件内容
            InputStream fileStream = readFileContent(request, fileInfo);
            if (fileStream == null) {
                return FileResponse.error(500, "Failed to read file");
            }
            
            // 构建下载响应
            return buildDownloadResponse(request, fileInfo, fileStream);
            
        } catch (Exception e) {
            return FileResponse.internalError("Download failed: " + e.getMessage());
        }
    }
    
    @Override
    public FileResponse handlePreview(FileRequest request) {
        try {
            // 验证请求
            if (validateRequest(request)) {
                return FileResponse.error(400, "Invalid request");
            }
            
            // 获取文件信息
            FileInfo fileInfo = getFileInfo(request);
            if (fileInfo == null) {
                return FileResponse.notFound();
            }
            
            // 检查文件权限
            if (!checkFilePermission(request, fileInfo)) {
                return FileResponse.error(403, "Access denied");
            }
            
            // 检查是否支持预览
            if (!isSupportPreview(fileInfo)) {
                return FileResponse.error(415, "File type not supported for preview");
            }
            
            // 读取文件内容
            InputStream fileStream = readFileContent(request, fileInfo);
            if (fileStream == null) {
                return FileResponse.error(500, "Failed to read file");
            }
            
            // 构建预览响应
            return buildPreviewResponse(request, fileInfo, fileStream);
            
        } catch (Exception e) {
            return FileResponse.internalError("Preview failed: " + e.getMessage());
        }
    }
    
    @Override
    public boolean supportFormat(String extension) {
        if (extension == null) {
            return false;
        }
        return MIME_TYPE_MAP.containsKey(extension.toLowerCase());
    }
    
    @Override
    public String getContentType(String extension) {
        return getMimeType(extension);
    }
    
    /**
     * 验证请求参数
     * 子类可以重写此方法添加自定义验证逻辑
     */
    protected boolean validateRequest(FileRequest request) {
        return request == null ||
                request.getFilePath() == null || request.getFilePath().trim().isEmpty()
                ;
    }


    
    /**
     * 检查文件是否支持预览
     */
    protected boolean isSupportPreview(FileInfo fileInfo) {
        String extension = getFileExtension(fileInfo.getFileName());
        if (extension == null) {
            return false;
        }
        
        String mimeType = getMimeType(extension);
        
        // 支持预览的文件类型
        return mimeType.startsWith("image/") || 
               mimeType.startsWith("text/") ||
               mimeType.equals("application/pdf") ||
               mimeType.startsWith("video/") ||
               mimeType.startsWith("audio/") ||
               mimeType.equals("application/vnd.apple.mpegurl"); // 支持 m3u8 预览
    }
    
    /**
     * 构建下载响应
     */
    protected FileResponse buildDownloadResponse(FileRequest request, FileInfo fileInfo, InputStream fileStream) {
        FileResponse response = FileResponse.success();
        response.setInputStream(fileStream);
        response.setContentLength(fileInfo.getFileSize());
        
        // 设置下载响应头
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/octet-stream");
        headers.put("Content-Disposition", "attachment; filename=\"" + encodeFileName(fileInfo.getFileName()) + "\"");
        headers.put("Content-Length", String.valueOf(fileInfo.getFileSize()));
        headers.put("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.put("Pragma", "no-cache");
        headers.put("Expires", "0");
        
        // 处理Range请求（断点续传）
        if (request.getRangeHeader() != null) {
            handleRangeRequest(request,response, fileInfo, headers);
        }
        
        response.setHeaders(headers);
        return response;
    }
    
    /**
     * 构建预览响应
     */
    protected FileResponse buildPreviewResponse(FileRequest request, FileInfo fileInfo, InputStream fileStream) {
        FileResponse response = FileResponse.success();
        response.setInputStream(fileStream);
        response.setContentLength(fileInfo.getFileSize());
        
        // 设置预览响应头
        Map<String, String> headers = new HashMap<>();
        String extension = getFileExtension(fileInfo.getFileName());
        String mimeType = getMimeType(extension);
        
        headers.put("Content-Type", mimeType);
        headers.put("Content-Length", String.valueOf(fileInfo.getFileSize()));

        // 对于 m3u8 文件，设置特殊的缓存控制
        if (mimeType.equals("application/vnd.apple.mpegurl")) {
            headers.put("Cache-Control", "no-cache, no-store, must-revalidate");
            headers.put("Pragma", "no-cache");
            headers.put("Expires", "0");
        } else {
            headers.put("Cache-Control", "public, max-age=3600000"); // 其他文件缓存1W小时
        }

        headers.put("Content-Disposition", "inline; filename=\"" + encodeFileName(fileInfo.getFileName()) + "\"");
        
        // 处理Range请求（视频/音频流媒体支持）
        if (request.getRangeHeader() != null && (mimeType.startsWith("video/") || mimeType.startsWith("audio/"))) {
            handleRangeRequest(request,response, fileInfo, headers);
        }
        
        response.setHeaders(headers);
        return response;
    }
    
    /**
     * 处理Range请求（断点续传/流媒体）
     */
    protected void handleRangeRequest(FileRequest request,FileResponse response, FileInfo fileInfo, Map<String, String> headers) {
        String rangeHeader = request.getRangeHeader();
        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
                String range = rangeHeader.substring(6);
                String[] ranges = range.split("-");
                
                long start = 0;
                long end = fileInfo.getFileSize() - 1;
                
                if (ranges.length > 0 && !ranges[0].isEmpty()) {
                    start = Long.parseLong(ranges[0]);
                }
                if (ranges.length > 1 && !ranges[1].isEmpty()) {
                    end = Long.parseLong(ranges[1]);
                }
                
                // 确保范围有效
                if (start >= 0 && end < fileInfo.getFileSize() && start <= end) {
                    headers.put("Content-Range", "bytes " + start + "-" + end + "/" + fileInfo.getFileSize());
                    long contentLength = end - start + 1;
                    headers.put("Content-Length", String.valueOf(contentLength));
                    headers.put("Accept-Ranges", "bytes");

                    /*
                    Safari 是少数完全遵循 HTTP 规范的浏览器，尤其对视频 Range 请求有两点要求：
                    必须支持 Range: bytes=... 请求和 206 Partial Content 响应
                    对非标准响应组合会直接拒绝加载（不像 Chrome 会容忍）
                    */
                    response.setStatusCode(206);
                    //  Content-Length 应该为请求的字节长度（也就是 2），但你返回了整个文件长度，这是不符合标准的！
                    response.setContentLength(contentLength);
                }
        }
    }
    
    /**
     * 获取文件扩展名
     */
    protected String getFileExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return null;
        }
        
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex + 1).toLowerCase();
        }
        
        return null;
    }
    
    /**
     * 获取MIME类型
     */
    protected String getMimeType(String extension) {
        if (extension == null) {
            return "application/octet-stream";
        }
        
        return MIME_TYPE_MAP.getOrDefault(extension.toLowerCase(), "application/octet-stream");
    }
    
    /**
     * 编码文件名（处理中文等特殊字符）
     */
    protected String encodeFileName(String fileName) {
        if (fileName == null) {
            return "download";
        }
        
        try {
            return URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
        } catch (Exception e) {
            return "download";
        }
    }



    // ========== 抽象方法，子类必须实现 ==========

    /**
     * 检查文件权限
     * 子类可以重写此方法添加自定义权限检查逻辑
     */
    protected abstract boolean checkFilePermission(FileRequest request, FileInfo fileInfo);


    /**
     * 获取文件信息
     * 子类实现具体的文件信息获取逻辑
     * 
     * @param request 文件请求
     * @return 文件信息，如果文件不存在返回null
     */
    protected abstract FileInfo getFileInfo(FileRequest request);
    
    /**
     * 读取文件内容
     * 子类实现具体的文件读取逻辑
     * 
     * @param request 文件请求
     * @param fileInfo 文件信息
     * @return 文件输入流，如果读取失败返回null
     */
    protected abstract InputStream readFileContent(FileRequest request, FileInfo fileInfo);
    

}
